from OpenSteer import OpenSteer
from util.interfaces import IDestroyable,ITimerUpdatable,ISelectable


import ogre.renderer.OGRE as ogre
import ogre.io.OIS as OIS

class LandVehicule(OpenSteer.SimpleVehicule):
    
    def __init__(self):
        opensteer.SimpleVehicule.__init__(self)
        self.forcedNormal = ogre.Vector3.UNIT_Y
        self.position=(0,0,0)
        
        
        
    def setPosition(self,p):
        self.position=p
        
    def getPosition(self):
        return ogre.Vector3(self.position)
    
    Position=property(getPosition,setPosition)

    def getOrientation(self):
        return ogre.Quaternion(ogre.Vector3(self.forward()),\
                                ogre.Vector3(self.up()),\
                                ogre.Vector3(self.side()))

    def setOrientation(self,value):
        self.setForward(value.XAxis)
        self.setUp(value.YAxis)
        self.setSide(value.ZAxis)

    Orientation=property(getOrientation,setOrientation)
    
    def move(self, dt, target):

        if (target == ogre.Vector3.ZERO) :
            self.applySteeringForce(self.steerForWander(dt), dt)
        else:
            self.applySteeringForce(self.steerForSeek(target), dt)

        self.forceNormalUp()

    def slowDown(self, dt):
        if (self.speed() == 0): return
        if (self.speed() < 0.1):
            self.setSpeed(0.0)
        else:
            self.setSpeed(self.speed() * (1 - self.maxForce() * self.mass() * dt / 100))

        self.forceNormalUp()
    
    def forceNormalUp(self):
        """ A trick to make sure that the object is always up following the _forcedNormal,
        which is normally the Y_Axis vector"""
        self.setUp(self.forcedNormal)
        self.setForward(Utilities.perpendicularComponent(self.forward(), Vector3D(self.forcedNormal)))
        self.setSide(self.localRotateForwardToSide(self.forward()))
  
class BarrierPole(ITimerUpdatable,ISelectable,IDestroyable):
    
    def getSceneNode(self):
        return self.sceneNode
    
    def getPosition(self):
        if self.sceneNode is not None:
            return self.sceneNode.getPosition()
        return ogre.Vector3.UNIT_X
    
    def setPosition(self,iP):
        if self.sceneNode is not None:
            self.sceneNode.setPosition=iP(iP)
    
    
    def getName(self):
        return self.name
    
    
    
    def __init__(self, iWorld,  iName,  iSceneNode, iObstacleMap):
        print "init BP"
        IDestroyable.__init__(self,100)
        self.name=iName
        self.world=iWorld
        self.sceneNode=iSceneNode
        self.obstacleMap=iObstacleMap
        
        self.peers=[]
        self.entity=iWorld.MainWindow.SceneMgr.createEntity(iName, "sphere.mesh")
        self.entity.QueryFlags =world.World.SELECTABLE_MASK|xenocide.World.HUMAN_MASK

        self.sceneNode.setScale(0.1, 0.1, 0.1)
        self.sceneNode.attachObject(self.entity)
        self.entity.setVisible =( False)
        self.billboard = GlowingBillboard(self.sceneNode.createChildSceneNode(),\
                    iName)
        
    def timerUpdate(self, dt):
    
        if self.billboard is not None:
            return self.billboard.timerUpdate(dt)
        return True

    def __del__(self):
        del self.billboard
        self.world.MainWindow.SceneMgr.destroyEntity(self.entity)
        self.world.MainWindow.SceneMgr.destroySceneNode(self.sceneNode.Name)
        
    def doMouseUp(self, iPos, iButton, isShift, isCtrl, isAlt, iQr, iMO):
        if isAlt and iButton==OIS.MouseButtonID.MB_Right:
            self.world.destroy(self)
            return True
        
        return False
    
    
class BarrierLink:
    
    def getPeers(self):
        return self.peers
    
    def getName(self):
        return self.name
    
    Name=property(getName)
    Peers=property(getPeers)
    
    def __init__(self,iName, ibp1,ibp2, iWorld):
        self.world=iWorld
        self.name=iName
        
        self.peers=(ibp1,ibp2)
        
        v=ibp1.getPosition()-ibp2.getPosition()
        
        self.sceneNode=iWorld.MainWindow.SceneMgr.RootSceneNode\
            .createChildSceneNode(iName,ibp2.SceneNode.getWorldPosition())
        
        self.sceneNode.setInheritScale ( False)
        self.sceneNode.setScale(ogre.Vector3.UNIT_SCALE)
        self.sceneNodeChild = self.sceneNode.createChildSceneNode(v * 0.5)
            
        self.entity=iWorld.MainWindow.SceneMgr.createEntity(iName , "barrierlink")
        self.entity.setMaterialName ( "Alpha/Barrier")
        
        q = ogre.Vector3.UNIT_X.getRotationTo(v.normalisedCopy())

        self.sceneNodeChild.setOrientation ( q)
        self.sceneNodeChild.setScale(v.length() / 32, 1, 1)

        self.sceneNodeChild.attachObject(self.entity)
        
    def isLinking(self,iBP):
        return iBP in self.peers
    
    def __del__(self):
        self.sceneNodeChild.detachAllObjects()
            
        self.sceneNode.detachAllObjects()
        self.world.MainWindow.SceneMgr.destroyEntity(self.entity)
        self.world.MainWindow.SceneMgr.destroySceneNode(self.sceneNodeChild.get(Name))
        self.world.MainWindow.SceneMgr.destroySceneNode(self.sceneNode.getName())

       
class GlowingBillboard(ITimerUpdatable):
    
    
    def __init__(self,iSceneNode,iName):
        self.__name=iName
        self.__sceneNode=iSceneNode
        
        self.__nodeMaxScale = 0.8
        self.__nodeScale=self.__nodeMinScale = 0.1
        
        self.__sceneNode.setInheritScale = (False)
        self.__sceneNode.setInheritOrientation (False)
        self.__sceneNode.setScale(ogre.Vector3.UNIT_SCALE * self.__nodeMinScale)
        self.__frequency=4
        self.__nodeScaleStep = (self.__nodeMaxScale-self.__nodeMinScale) * self.__frequency
        

        self.__billboardSet = ogre.BillboardSet(iName)
        self.__billboardSet.setMaterialName ( "Alpha/BlueLight")

        self.__sceneNode.attachObject(self.__billboardSet)
        self.__billBoard = self.__billboardSet.createBillboard\
            (ogre.Vector3.ZERO)
        
    def timerUpdate(self, dt):
    
        self.__nodeScale = self.__nodeScale + self.__nodeScaleStep*dt
        if (self.__nodeScale >= self.__nodeMaxScale) or (self.__nodeScale <= self.__nodeMinScale):
            self.__nodeScaleStep = -self.__nodeScaleStep
        self.__sceneNode.setScale(ogre.Vector3.UNIT_SCALE * self.__nodeScale)
        
        return True
    def __del__(self):
        self.__billboardSet.dispose()
        

class LightTower:
    
    def __init__(self,iParent, iName, iRange,  iHeight):
        
        self.parent = iParent
        self.yaw = 0.0
        self.cycle = 0.0
        self.direction = 3.141592
        self.amplitude = 1.0
        self.name = iName
        self.range = iRange
        self.light=self.parent.world.MainWindow.SceneMgr.createLight(iName\
            + "_Light1")
        self.light.Type = ogre.Light.LightTypes.LT_SPOTLIGHT
        self.light.DiffuseColour = ogre.ColourValue.Green
        print "init LV : "
        self.FOV=ogre.Degree(45)
        #mParent.ContainingWorld.MainWindow.log("mLight :" + mLight.AttenuationRange + " / " + mLight.AttenuationConstant + " / " + mLight.AttenuationLinear + " / " + mLight.AttenuationQuadric);
        
        self.light.setAttenuation(self.range, 0.0001,0.001, 0.0000004)
        #mParent.ContainingWorld.MainWindow.log("mLight :" + mLight.AttenuationRange + " / " + mLight.AttenuationConstant + " / " + mLight.AttenuationLinear + " / " + mLight.AttenuationQuadric);
        self.light.setSpotlightRange(ogre.Degree(30), self.FOV,1.0)
        
        q=ogre.Quaternion()
        q.FromAngleAxis(ogre.Radian(ogre.Degree(0)), ogre.Vector3.NEGATIVE_UNIT_Z)
        self.light.Direction = q*ogre.Vector3.UNIT_X
        

        self.sceneNode = self.parent.node.createChildSceneNode()
        self.sceneNode.setInheritScale (False)
        self.sceneNode.translate(1.0, iHeight, 0)
        self.sceneNode.attachObject(self.light)
        
    def getName(self):
        return self.name
    
    def setDirAmpl(self, iDest):
        
            minLength = 20.0
            
            iDest.y=self.parent.getPosition().y
            src = self.parent.getOrientation() * ogre.Vector3.UNIT_X
            iDiff= iDest - self.parent.getPosition()
            length = iDiff.length()
            if length < 10.0 : length=10.0
            
            dp=src.dotProduct(iDiff)
            right = src.crossProduct(ogre.Vector3.NEGATIVE_UNIT_Y)

            th = ogre.Math.ACos(dp / length).valueRadians()

            if right.dotProduct(iDiff)<0 :
                self.direction =   -th
            else :
                self.direction =   th

            self.amplitude = 3.14159/ (length*length) * minLength*minLength
            
    def doTimer(self,dt):
    
        self.cycle = self.cycle+dt
        self.cycle = self.cycle%(3.141592 * 2)
        
        self.yaw = self.direction + ogre.Math.Cos(self.cycle) * self.amplitude

        
        q = ogre.Quaternion(self.yaw, ogre.Vector3.UNIT_Y)
        self.sceneNode.setOrientation(q) 
        
    def __del__(self):
        self.parent.ContainingWorld.MainWindow.SceneMgr.destroyLight(self.light)
        self.light.dispose()
        self.parent.ContainingWorld.MainWindow.SceneMgr.destroySceneNode(self.sceneNode.getName())
        self.sceneNode.dispose()      

class MobileObject(IDestroyable,ITimerUpdatable,ISelectable):
    
    CONTACT_DISTANCE=20
    BEHAVIOUR_FOLLOW_PATH=1
    BEHAVIOUR_IDLE=2
    
    def getEntity(self):
        return self.entity
    def setEntity(self,value):
        if value is ogre.Entity :
            self.entity=value
    
    MyEntity=property(getEntity,setEntity)
    
    def getWorld(self):
        return self.world
    def setWorld(self,value):
        print("Read only!")
        
    ParentWorld=property(getWorld,setWorld)
    
    def getName(self):
        return self.name
    def setName(self,value):
        self.name=value

    
    def getNode(self):
        return self.node
    def setNode(self,value):
        if value is ogre.SceneNode :
            self.node=value
    
    MainNode=property(getNode,setNode)
    
    def getPosition(self):
        return self.node.getPosition()
    def setPosition(self,value):
        self.node.setPosition(value)
        
    Position=property(getPosition,setPosition)    
    
    def getOrientation(self):
        return self.node.getOrientation()
    def setOrientation(self,value):
        self.node.setOrientation(value)
        
    
    def getSelected(self):
        return self.selected
    def setSelected(self,value):
        self.selected=value
        
        ## Not very nice...
        ## asks the world parent object to move the selection marker
        if value:
            if self.world.MainWindow.mBBS.ParentSceneNode is not None:
                print("***old : " + self.world.MainWindow.mBBS.WorldPosition)

                self.world.MainWindow.mBBS.ParentSceneNode.DetachObject("selection set")
                print("detaching mBBS")
            
            self.nodeSelect.AttachObject(self.world.MainWindow.mBBS)
            print(self.world.MainWindow.mBBS.getWorldPosition())
            print(self.world.MainWindow.mBBS.getBillboard(0).getPosition())
        else:
           print("***old : " + self.world.MainWindow.mBBS.getWorldPosition())
           self.nodeSelect.detachObject(self.world.MainWindow.mBBS)
        
     
    Selected=property(getSelected,setSelected)
    
    def flatDistanceTo(self,iMO):
        pos = ogre.Vector2(self.node.getPosition().x-iMO.getPosition().x, self.node.getPosition().z-iMO.getPosition().z)
        return pos.Length
    
    def getSize(self):
        return self.size
    def setSize(self,value):
        self.size=value
    Size=property(getSize,setSize)
    
    def __init__(self,iWorld, iName, iPos, iOrient):
        print ("Entering MobileObject init,with following values ",iWorld,iName,iPos,iOrient)
        self.world=iWorld
        self.name=iName
        
        self.node = self.world.MainWindow.SceneMgr.getRootSceneNode()\
            .createChildSceneNode()

        self.size=20
        
        self.entity = self.world.MainWindow.SceneMgr\
            .createEntity(iName, "sphere.mesh")
        print "sofarsogood1"
        self.nodeEntity = self.node.createChildSceneNode()
        self.nodeEntity.attachObject(self.entity)
        self.nodeEntity.setScale(1, 1, 1)
        self.nodeSelect = self.node.createChildSceneNode(ogre.Vector3.UNIT_Y * 20.0)
        self.nodeSelect.setInheritScale =( False )

        self.nodeSelectMaxScale = 1.0
        self.nodeSelectMinScale = 0.3
        self.nodeSelect.setScale(ogre.Vector3.UNIT_SCALE * self.nodeSelectMinScale)
        self.nodeSelectScale = self.nodeSelectMinScale
        self.nodeSelectScaleStep = self.nodeSelectMaxScale / 5.0

        print "sofarsogood"
        self.setPosition(iPos)
        self.setOrientation(iOrient)
        
        self.dead=False
            
        self.selected=False
        print ("End of MobileObject init")
        
        
    

    def __del__(self):
        self.dead=True
        self.world.MainWindow.SceneMgr.destroyEntity(self.entity)
        self.world.MainWindow.SceneMgr.destroySceneNode(self.node.getName())
   
    def timerUpdate(self,iTimeInterval):
        if self.Selected:
            self.nodeSelectScale = self.nodeSelectScale + self.nodeSelectScaleStep
            if (self.nodeSelectScale >= self.nodeSelectMaxScale) or (self.nodeSelectScale <= self.nodeSelectMinScale):
                self.nodeSelectScaleStep = -self.nodeSelectScaleStep
            
            self.nodeSelect.setScale(ogre.Vector3.UNIT_SCALE * self.nodeSelectScale)
        
        return True

class T_WayPoint:
    
        def setPosition(self,value):
            self.pos=value

        def getPosition(self):
            return self.pos
        
        def __init__(self, iWorld, iPos,iName):
            self.world=iWorld
            self.sceneNode=self.world.MainWindow.SceneMgr.getRootSceneNode()\
                .createChildSceneNode()
            self.entity=self.world.MainWindow.SceneMgr\
                .createEntity(iName, "sphere.mesh")
            self.sceneNode.setScale(0.1, 0.1, 0.1)
            self.sceneNode.setPosition(iPos)
            self.sceneNode.attachObject(self.entity)
            self.pos = iPos
            
        def __del__(self):
            self.sceneNode.detachAllObjects()
            
            self.world.MainWindow.SceneMgr.destroySceneNode(self.sceneNode.getName())
            self.sceneNode = None
            if self.entity :
                self.world.MainWindow.SceneMgr.destroyEntity(self.entity)
                self.entity = None
            
        

		
class MobileActor(MobileObject):
    

    
    
    def __init__(self,iWorld, iName, iPos, iOrient, iMeshName):
        
        self.entity=None
        self.dead=False
        MobileObject.__init__(self,iWorld, iName, iPos, iOrient)

        self.lv=LandVehicule()

        self.waypoints=None
        self.wpcounter=0
        self.world=iWorld
        
        if self.entity is not None:
            
            self.world.MainWindow.SceneMgr.destroyEntity(self.entity)
        
        self.entity = self.world.MainWindow.SceneMgr.createEntity(iName+"ent", iMeshName)

        self.nodeEntity.attachObject(self.entity)

        self.nodeEntity.setScale(5, 5, 5)

            
        self.behaviour=MobileActor.BEHAVIOUR_IDLE

        
        self.lv.setSpeed(25.0)
        self.lv.setMaxSpeed(100.0)
        self.lv.setMaxForce(20.0)
        self.lv.setBankingBehaviour ( True )
        self.lv.setMass(0.3)
        self.lv.setOrientation(self.node.getOrientation())
        
        
        self.entity.setQueryFlags(World.SELECTABLE_MASK|World.HUMAN_MASK)
        print "Light Tower ?"

        self.lightTower = LightTower(self, iName + "_LT",300.0, 10.0)
        print "test"
            
    def __del__(self):
        self.dead=True
        self.clearPath()
        del self.lightTower
        MobileObject.__del__()
        
           
    def doMouseDown(self,loc, iButton, iShift,  iCtrl,  iAlt,  iQr,  iMO):
       
        
        if iButton==OIS.MouseButtonID.MB_Right:
                print("Treated MouseDown message : MB_RIGHT")
                if iCtrl:
                    self.lightTower.setDirAmpl(loc)
                    return True
                

                if iQr==MainWindow.QUERY_GROUND:
                    loc.y = loc.y+10
                    self.addWayPoint(loc, not iShift,iAlt)
                    print("Treated MouseDown message : MB_RIGHT")
                    return True
                

           
        return False
        
    def doMouseUp(self,loc, iButton,  iShift,  iCtrl,  iAlt,  iQr,  iMO):
        
        if iButton==OIS.MouseButtonID.MB_Right:
            print("Treated MouseDown message : MB_RIGHT")
            if iCtrl:
            
                self.lightTower.setDirAmpl(loc)
                return True

            if iQr == MainWindow.QUERY_GROUND:
                loc.y = loc.y+10
                self.addWayPoint(loc, not iShift, iAlt)
                print("Treated MouseDown message : MB_RIGHT")
                return True
        
        return False
    
    def timerUpdate(self, iTimeInterval):
        if self.dead:
            print("DeadManWalking!")
            return True
        returnValue = MobileObject.timerUpdate(iTimeInterval)
        self.moveOnGround(iTimeInterval)

        return returnValue
    
    
    def moveOnGround(self, iTimeInterval):
        alt = self.node.getPosition().y
        self.moveFree(iTimeInterval)
        norm=ogre.Vector3.UNIT_Y
        
        self.setAltitude(alt)
        self.forceNormal(norm)
        if self.lightTower:
            self.lightTower.doTimer(iTimeInterval)
        
    def moveFree(self, timeInterval):
        """ Move without constriaint, typical for flying objects..."""

        if self.behaviour==MobileObject.BEHAVIOUR_FOLLOW_PATH:

            if len(self.waypoints)>0:
                dest=self.waypoints(0).pos
                self.lv.move(timeInterval,dest)
                dist=dest-self.lv.getPosition()
                if dist.length() < self.CONTACT_DISTANCE:
                    wp=self.waypoints.pop(0)
                    del wp
                    if len(self.waypoints)==0:
                        self.behaviour=MobileObject.BEHAVIOUR_IDLE

		if self.behaviour==MobileObject.BEHAVIOUR_IDLE: 
                    self.lv.slowDown(timeInterval)    


        self.node.setPosition(self.lv.getPosition())
        self.node.setOrientation(self.lv.getOrientation())
    
        
            
    def clearPath(self):
        for i in range(len(self.waypoints)-1, 0, -1):
            tmp=self.waypoints.pop()
            del tmp
        self.behaviour = MobileActor.BEHAVIOUR_IDLE
        
    def addWayPoint(self, iWP,  clear, useAStar):
        if self.behaviour==MobileActor.BEHAVIOUR_IDLE or self.behaviour==MobileActor.BEHAVIOUR_FOLLOW_PATH:
            if clear :
                self.clearPath()
            
            self.behaviour=MobileObject.BEHAVIOUR_FOLLOW_PATH
            
            # Check if is using a path finding algorythm
            if useAStar :
            
                lwp=self.world.compute2DPath(self.node.getPosition(),iWP,self.world.MyMap)
                # if no path can be found
                if not lwp:
                    self.behaviour = MobileActor.BEHAVIOUR_IDLE
            
                else:
                    self.behaviour = MobileActor.BEHAVIOUR_FOLLOW_PATH
                    for v in lwp:
                        wp=T_WayPoint(self.world,v,self.getName()+"_wp#"+(self.wpcounter))
                        self.wpcounter=self.wpcounter+1
                        self.waypoints.append(wp)

            else: # if not using path finding, just add the point.
                wp=T_WayPoint(self.world,iWP,self.getName()\
                    +"_wp#"+(self.wpcounter))
                self.wpcounter=self.wpcounter+1
                self.waypoints.append(wp)
            
    def setAltitude(self, alt):
        setPosition ( ogre.Vector3(self.getPosition().x, alt, self.getPosition().z))
        
    def forceNormal(self, n):
        self.lv.ForcedNormal = n
    
    